﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Logging;
using StyleMVVM.Utilities;
using StyleMVVM.ViewModel;
#if NETFX_CORE
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
#else
using System.Windows;
using System.Windows.Navigation;
using System.Windows.Media;

#endif

namespace StyleMVVM.View
{
	/// <summary>
	/// This class offers Dependency properties that allow you to bind a ViewModel
	/// to a Views DataContext by setting the Name property in XAML
	/// </summary>
	public sealed class ViewModel
	{
		//private static IViewModelResolutionService resolutionService;
		private static string supplementalString = typeof(ViewModel).FullName;

		/// <summary>
		/// Gets the name of the ViewModel for the specified view
		/// </summary>
		/// <param name="obj">the view</param>
		/// <returns>the ViewModel Name</returns>
		public static string GetName(DependencyObject obj)
		{
			return (string)obj.GetValue(NameProperty);
		}

		/// <summary>
		/// Sets the ViewModel name for the provided View
		/// </summary>
		/// <param name="obj"></param>
		/// <param name="value"></param>
		public static void SetName(DependencyObject obj, string value)
		{
			obj.SetValue(NameProperty, value);
		}

		public static DependencyProperty NameProperty
		{
			get { return InternalNameProperty; }
		}

		/// <summary>
		/// Assigns a ViewModel Name to a View and is the Linkage that connects the
		/// ViewModel to the Views data context (Done at name change)
		/// </summary>
		internal static readonly DependencyProperty InternalNameProperty =
			DependencyProperty.RegisterAttached("Name",
			                                    typeof(string),
			                                    typeof(ViewModel),
			                                    new PropertyMetadata(null, NameChanged));

		private static async void NameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			if (d is FrameworkElement)
			{
				if (DesignMode.IsEnabled)
				{
					DesignTimeBootstrapper.CreateDesignTimeBootstrapper();
				}

				IViewModelResolutionService resolutionService = null;

				resolutionService =
					Bootstrapper.Instance.Container.LocateByType(typeof(IViewModelResolutionService)) as IViewModelResolutionService;

				if (resolutionService != null)
				{
					if (resolutionService.ResolveViewModel(d as FrameworkElement, e.NewValue as string) == false)
					{
						Logger.Error("Could not find ViewModel by the name of " + e.NewValue, supplementalString);
					}
				}
				else
				{
					Logger.Error("Could not find IViewModelResolutionService service", supplementalString);
				}
			}
			else
			{
				Logger.Error("ViewModel.Name can only be applied to FrameworkElement", supplementalString);
			}
		}

#if DOT_NET

		/// <summary>
		/// Gets the ViewModel Type associated with the view
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public static Type GetType(DependencyObject obj)
		{
			return (Type)obj.GetValue(TypeProperty);
		}

		/// <summary>
		/// Sets the ViewModel type associated with the View
		/// </summary>
		/// <param name="obj"></param>
		/// <param name="value"></param>
		public static void SetType(DependencyObject obj, Type value)
		{
			obj.SetValue(TypeProperty, value);
		}

		/// <summary>
		/// This property can be attach a ViewModel to a View's data context. This is a type safe way of locating a ViewModel instead of by string name
		/// </summary>
		public static readonly DependencyProperty TypeProperty =
			DependencyProperty.RegisterAttached("Type", typeof(Type), typeof(ViewModel), new PropertyMetadata(null, TypeChanged));

		private static void TypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			if (d is FrameworkElement)
			{
				if (DesignMode.IsEnabled)
				{
					DesignTimeBootstrapper.CreateDesignTimeBootstrapper();
				}

				IViewModelResolutionService resolutionService = null;

				resolutionService =
					Bootstrapper.Instance.Container.LocateByType(typeof(IViewModelResolutionService)) as IViewModelResolutionService;

				if (resolutionService != null)
				{
					resolutionService.ResolveViewModel(d as FrameworkElement, e.NewValue as Type);
				}
				else
				{
					Logger.Error("Could not find IViewModelResolutionService service");
				}
			}
			else
			{
				Logger.Error("ViewModel.Name can only be applied to FrameworkElement");
			}
		}

#endif
	}
}